﻿/**
 * Peter
 * Created by: Peter Development Team
 *    http://peter.codeplex.com/
 * 
 * GNU General Public License version 2 (GPLv2)
 *    http://peter.codeplex.com/license
 *
 *  This code is provided on an AS IS basis, with no WARRANTIES,
 *  CONDITIONS or GUARANTEES of any kind.
 *  
 **/

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Input;
using Peter.Common;
using Peter.Common.Interfaces;
using Peter.Common.MainMenu;
using Peter.Common.Services;
using Peter.Common.Utilities;
using Peter.Logic.Interfaces;

namespace Peter.Logic
{
   /// <summary>
   /// Quick open logic.
   /// </summary>
   [Export (typeof (ModelQuickOpen))]
   public class ModelQuickOpen : ViewModelBase
   {
      private bool m_IsQuickOpenOpen;
      private readonly MainMenuTextBox m_TextBox;
      private int m_SelectedIndex;

      /// <summary>
      /// Initializes the quick open object.
      /// </summary>
      public ModelQuickOpen ()
      {
         this.QuickOpenItems = new ObservableCollection <QuickOpenItem> ();
         this.m_TextBox = new MainMenuTextBox { Priority = 0, Width = 150, Title = "Quick Open (Ctrl+Q)" };
         this.m_TextBox.TextChanged += this.OnTextChanged;
         this.m_TextBox.KeyCommand = new RelayCommand (this.OnKeyPressed);
         this.m_TextBox.Command = new RelayCommand (this.OnExecute);
         this.m_TextBox.FocusCommand = new RelayCommand (this.OnFocusChanged);
         var app = ServiceManager.Get <IAppMain> ();
         if (app != null)
         {
            app.MainWindow.CommandBindings.Add (
               new CommandBinding (PeterApplicationCommands.QuickOpen, this.OnQuickOpen));
         }
      }

      /// <summary>
      /// Occurs when the focus of the text box changes.
      /// </summary>
      /// <param name="obj">Focus Event.</param>
      private async void OnFocusChanged (object obj)
      {
         var focusType = (FocusExtension.FocusEvent) obj;
         if (focusType == FocusExtension.FocusEvent.LostFocus)
            this.IsQuickOpenOpen = false;
         else if (!string.IsNullOrEmpty (this.m_TextBox.Text))
            await SearchProviders (this.m_TextBox.Text);
      }

      /// <summary>
      /// Occurs when the quick open command is executed.
      /// </summary>
      /// <param name="sender">Sender of the command.</param>
      /// <param name="e">ExecutedRoutedEventArgs</param>
      private void OnQuickOpen (object sender, ExecutedRoutedEventArgs e)
      {
         if (this.IsFocused)
            this.IsFocused = false;
         this.IsFocused = true;
      }

      /// <summary>
      /// Gets or Sets if the quick open is focused or not.
      /// </summary>
      public bool IsFocused
      {
         get { return this.m_TextBox.IsFocused; }
         set { this.m_TextBox.IsFocused = value; }
      }

      /// <summary>
      /// Gets the list of quick open providers.
      /// </summary>
      [ImportMany (typeof (IQuickOpenProvider))]
      public IEnumerable <IQuickOpenProvider> QuickOpenProviders { get; set; }

      /// <summary>
      /// Gets the options menu item.
      /// </summary>
      [Export (typeof (IMainMenuQuickAccessItem))]
      public IMainMenuQuickAccessItem QuickOpenMenuItem
      {
         get { return this.m_TextBox; }
      }

      /// <summary>
      /// Gets the instance of peter.
      /// </summary>
      [Import (typeof (IPeter))]
      public IPeter Peter { get; set; }

      /// <summary>
      /// Gets if the quick open is open or not.
      /// </summary>
      public bool IsQuickOpenOpen
      {
         get { return this.m_IsQuickOpenOpen; }
         set
         {
            if (this.m_IsQuickOpenOpen != value)
            {
               this.m_IsQuickOpenOpen = value;
               this.OnPropertyChanged ("IsQuickOpenOpen");
            }
         }
      }

      /// <summary>
      /// Gets or Sets the selected index for the quick open list.
      /// </summary>
      public int SelectedIndex
      {
         get { return this.m_SelectedIndex; }
         set
         {
            if (this.m_SelectedIndex != value)
            {
               this.m_SelectedIndex = value;
               this.OnPropertyChanged ("SelectedIndex");
            }
         }
      }

      /// <summary>
      /// Gets the list of quick open items.
      /// </summary>
      public ObservableCollection<QuickOpenItem> QuickOpenItems { get; private set; }

      /// <summary>
      /// Occurs when the quick open text changes.
      /// </summary>
      /// <param name="sender">Quick Open Textbox Menu item.</param>
      /// <param name="args">TextEventArgs</param>
      private async void OnTextChanged (object sender, TextEventArgs args)
      {
         this.SelectedIndex = 0;
         if (!string.IsNullOrEmpty (args.Text))
         {
            await SearchProviders (args.Text);
         }
         else
         {
            this.IsQuickOpenOpen = false;
         }
      }

      /// <summary>
      /// Searches the quick open providers.
      /// </summary>
      /// <param name="text">Text to send to providers to search for.</param>
      /// <returns>The search task.</returns>
      private async Task SearchProviders (string text)
      {
         this.IsQuickOpenOpen = true;
         this.QuickOpenItems.Clear ();
         var tasks = new Collection <Task <IEnumerable <QuickOpenItem>>> ();
         foreach (var provider in this.QuickOpenProviders)
            tasks.Add (provider.GetQuickOpenItemsAsync (text));

         foreach (var task in tasks)
         {
            var quickOpenItems = await task;
            foreach (var quickOpenItem in quickOpenItems)
            {
               this.QuickOpenItems.Add (quickOpenItem);
            }
         }
      }

      /// <summary>
      /// Occurs when the quick open executes an item.
      /// </summary>
      /// <param name="obj">Command parameter, not used.</param>
      private void OnExecute (object obj)
      {
         if (this.SelectedIndex >= 0 && this.SelectedIndex < this.QuickOpenItems.Count)
         {
            var item = this.QuickOpenItems [this.SelectedIndex];
            var results = item.Execute ();
            if (results.ResultType == QuickOpenResultType.Finished)
               this.m_TextBox.Text = string.Empty;
            else if (results.ResultType == QuickOpenResultType.ReplaceText)
            {
               this.m_TextBox.Text = results.ReplaceText;
               this.m_TextBox.CaretPosition = results.ReplaceText.Length;
            }
            else if (results.ResultType == QuickOpenResultType.OpenInPeter)
            {
               this.m_TextBox.Text = string.Empty;
               this.Peter.Open (results.FileToOpen);
            }
         }
         else if (File.Exists (this.m_TextBox.Text))
         {
            this.m_TextBox.Text = string.Empty;
            this.Peter.Open (this.m_TextBox.Text);
         }
      }

      /// <summary>
      /// Occurs when a key is pressed in the quick open text box.
      /// </summary>
      /// <param name="obj"></param>
      private void OnKeyPressed (object obj)
      {
         var keyArgs = obj as KeyEventArgs;
         if (keyArgs != null)
         {
            if (keyArgs.Key == Key.Down)
            {
               if (this.SelectedIndex >= this.QuickOpenItems.Count - 1)
                  this.SelectedIndex = 0;
               else
                  this.SelectedIndex++;
            }
            else if (keyArgs.Key == Key.Up)
            {
               if (this.SelectedIndex == 0)
                  this.SelectedIndex = this.QuickOpenItems.Count - 1;
               else
                  this.SelectedIndex--;
            }
         }
      }
   }
}
